/*
Copyright (C) 2001-2002 Charles Hollemeersch

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

PENTA: the whole file is freakin penta...

Same as gl_bumpmap.c but geforce3&4 optimized
These routines require 4 texture units an some need nvidia shaders

Most lights reqire 2 passes this way
1 diffuse
2 specular

If a light has a cubemap filter it requires 3 passes
1 attenuation
2 diffuse
3 specular
*/

#include "quakedef.h"

//<AWE> "diffuse_program_object" has to be defined static. Otherwise nameclash with "gl_bumpradeon.c".
static GLuint diffuse_program_object;
static GLuint specularalias_program_object; //He he nice name to type a lot

/*
Pixel shader for diffuse bump mapping does diffuse bumpmapping with norm cube, self shadowing & dist attent in
1 pass (thanx to the 4 texture units on a gf4)
*/
void GL_EnableDiffuseShaderGF3(qboolean world, vec3_t lightOrig)
{
    float invrad = 1 / currentshadowlight->radius;

    //tex 0 = normal map
    //tex 1 = nomalization cube map (tangent space light vector)
    //tex 2 = color map
    //tex 3 = (attenuation or light filter, depends on light settings but the actual
    //			register combiner setup does not change only the bound texture)
    GL_SelectTexture(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_CUBE_MAP_ARB);
    glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, atten1d_texture_object);

    GL_SelectTexture(GL_TEXTURE2_ARB);
    glEnable(GL_TEXTURE_2D);

    GL_SelectTexture(GL_TEXTURE3_ARB);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();
    if (currentshadowlight->filtercube)
    {
        glEnable(GL_TEXTURE_CUBE_MAP_ARB);
        glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, currentshadowlight->filtercube);
        GL_SetupCubeMapMatrix(world);
    }
    else
    {
        glEnable(GL_TEXTURE_3D);
        glBindTexture(GL_TEXTURE_3D, atten3d_texture_object);

        glTranslatef(0.5,0.5,0.5);
        glScalef(0.5,0.5,0.5);
        glScalef(invrad, invrad, invrad);
        glTranslatef(-lightOrig[0], -lightOrig[1], -lightOrig[2]);
    }
    GL_SelectTexture(GL_TEXTURE0_ARB);

    //combiner0 RGB: calculate
    //	(normal map = A) dot (norm cubemap = B) save in Spare0 RGB
    //	(color map = C) mul (light filter = D) save in Spare1 RGB
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
    qglCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE0_NV, GL_SPARE1_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE);

    //combiner0 Alpha: store 8*expand(tang space light vect z comp) into Spare0 Alpha (this is the selfshadow term)
    qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_BLUE);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
    qglCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    //Only if the light is not white we use a second combiner
    //this is when the light is at its full brightness (for flickering lights)
    //and doesn't have any color (other than white)
    if ((currentshadowlight->color[0] != 1) || (currentshadowlight->color[1] != 1) || (currentshadowlight->color[2] != 1))
    {
        qglCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 2);

        qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
        qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
        qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
        qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
        qglCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

        //alpha out = nothing
        qglCombinerOutputNV(GL_COMBINER1_NV, GL_ALPHA, GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
    }
    else
    {
        qglCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);
    }

    //final combiner: final RGB = (Spare 0 Alpha) * ( (Spare 0 RGB) * (Spare 1 RGB) )
    qglFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_E_TIMES_F_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_E_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_F_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

    //final cominer alpha doesn't really matter we use A dot B
    qglFinalCombinerInputNV(GL_VARIABLE_G_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);

    glEnable(GL_REGISTER_COMBINERS_NV);

    // Enable the vertex program.
    qglBindProgramNV( GL_VERTEX_PROGRAM_NV, diffuse_program_object );
    glEnable( GL_VERTEX_PROGRAM_NV );
}

void GL_DisableDiffuseShaderGF3(void)
{
    qglCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 1);

    //tex 0 = normal map
    //tex 1 = nomalization cube map (tangent space light vector)
    //tex 2 = color map
    //tex 3 = (attenuation or light filter, depends on light settings)
    GL_SelectTexture(GL_TEXTURE1_ARB);
    glDisable(GL_TEXTURE_CUBE_MAP_ARB);

    GL_SelectTexture(GL_TEXTURE2_ARB);
    glDisable(GL_TEXTURE_2D);

    GL_SelectTexture(GL_TEXTURE3_ARB);
    if (currentshadowlight->filtercube)
    {
        glDisable(GL_TEXTURE_CUBE_MAP_ARB);
    }
    else
    {
        glDisable(GL_TEXTURE_3D);
    }
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

    GL_SelectTexture(GL_TEXTURE0_ARB);
    glDisable(GL_REGISTER_COMBINERS_NV);
    glDisable(GL_VERTEX_PROGRAM_NV);
}

void GL_EnableSpecularShaderGF3(qboolean world, vec3_t lightOrig, qboolean alias)
{
    vec3_t  scaler = {0.5f, 0.5f, 0.5f};
    float   invrad = 1 / currentshadowlight->radius;

    //tex 0 = normal map
    //tex 1 = nomalization cube map (tangent space half angle)
    //tex 2 = color map
    //tex 3 = (attenuation or light filter, depends on light settings but the actual
    //			register combiner setup does not change only the bound texture)
    GL_SelectTexture(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_CUBE_MAP_ARB);
    glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, atten1d_texture_object);
    GL_SelectTexture(GL_TEXTURE2_ARB);

    glEnable(GL_TEXTURE_2D);

    GL_SelectTexture(GL_TEXTURE3_ARB);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();

    if (currentshadowlight->filtercube)
    {
        glEnable(GL_TEXTURE_CUBE_MAP_ARB);
        glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, currentshadowlight->filtercube);
        GL_SetupCubeMapMatrix(world);
    }
    else
    {
        glEnable(GL_TEXTURE_3D);
        glBindTexture(GL_TEXTURE_3D, atten3d_texture_object);

        glTranslatef(0.5,0.5,0.5);
        glScalef(0.5,0.5,0.5);
        glScalef(invrad, invrad, invrad);
        glTranslatef(-lightOrig[0], -lightOrig[1], -lightOrig[2]);
    }
    GL_SelectTexture(GL_TEXTURE0_ARB);

    qglCombinerParameteriNV(GL_NUM_GENERAL_COMBINERS_NV, 4);
    qglCombinerParameterfvNV(GL_CONSTANT_COLOR0_NV, &scaler[0]);

    //combiner0 RGB: calculate
    //	(normal map = A) dot (norm cubemap = B) save in Spare0 RGB
    //	(gloss map = C) mul (light filter = D) save in Spare1 RGB
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV , GL_ALPHA);
    qglCombinerInputNV(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
    qglCombinerOutputNV(GL_COMBINER0_NV, GL_RGB, GL_SPARE0_NV, GL_SPARE1_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE);

    //combiner0 Alpha: store 8*expand(tang space light vect z comp) into Spare1 Alpha (this is the selfshadow term)
    if (alias)
    {
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SECONDARY_COLOR_NV, GL_EXPAND_NORMAL_NV, GL_BLUE);
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV, GL_SECONDARY_COLOR_NV, GL_EXPAND_NORMAL_NV, GL_BLUE);
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
    }
    else
    {
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_BLUE);
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_C_NV, GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_BLUE);
        qglCombinerInputNV(GL_COMBINER0_NV, GL_ALPHA, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
    }
    qglCombinerOutputNV(GL_COMBINER0_NV, GL_ALPHA, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE1_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    //rgb = multipy light with color
    qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
    qglCombinerInputNV(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
    qglCombinerOutputNV(GL_COMBINER1_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    //combiner1 Alpha: calculate 2*((N'dotH)^2 - 0.5f) -> store in Spare0 Alpha ("raise" to an exponent)
    qglCombinerInputNV(GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);
    qglCombinerInputNV(GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);
    qglCombinerInputNV(GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_C_NV, GL_CONSTANT_COLOR0_NV, GL_SIGNED_NEGATE_NV, GL_BLUE);
    qglCombinerInputNV(GL_COMBINER1_NV, GL_ALPHA, GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_ALPHA);
    qglCombinerOutputNV(GL_COMBINER1_NV, GL_ALPHA, GL_DISCARD_NV, GL_DISCARD_NV, GL_SPARE0_NV, GL_SCALE_BY_TWO_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    //combiner2 Alpha: Raise specular further
    qglCombinerInputNV(GL_COMBINER2_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglCombinerInputNV(GL_COMBINER2_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglCombinerOutputNV(GL_COMBINER2_NV, GL_ALPHA, GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
    //combiner2 rgb: Do nothing
    qglCombinerOutputNV(GL_COMBINER2_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    //combiner3 Alpha: Raise specular further
    qglCombinerInputNV(GL_COMBINER3_NV, GL_ALPHA, GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglCombinerInputNV(GL_COMBINER3_NV, GL_ALPHA, GL_VARIABLE_B_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglCombinerOutputNV(GL_COMBINER3_NV, GL_ALPHA, GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);
    //combiner3 rgb: Do nothing
    qglCombinerInputNV(GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglCombinerInputNV(GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_B_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglCombinerInputNV(GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_C_NV, GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV , GL_ALPHA);
    qglCombinerInputNV(GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_D_NV, GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV , GL_RGB);
    qglCombinerOutputNV(GL_COMBINER3_NV, GL_RGB, GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV, GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE);

    //final combiner: final RGB = (Spare 0 Alpha) * ( (Spare 0 RGB) * (Spare 1 RGB) )
    qglFinalCombinerInputNV(GL_VARIABLE_A_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_ALPHA);
    qglFinalCombinerInputNV(GL_VARIABLE_B_NV, GL_E_TIMES_F_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_C_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_D_NV, GL_ZERO, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_E_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);
    qglFinalCombinerInputNV(GL_VARIABLE_F_NV, GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB);

    //final cominer alpha doesn't really matter we use A dot B
    qglFinalCombinerInputNV(GL_VARIABLE_G_NV, GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE);

    glEnable(GL_REGISTER_COMBINERS_NV);

    // Enable the vertex program.
    if (alias)
    {
        qglBindProgramNV( GL_VERTEX_PROGRAM_NV, specularalias_program_object );
    }
    else
    {
        qglBindProgramNV( GL_VERTEX_PROGRAM_NV, diffuse_program_object );
    }
    glEnable( GL_VERTEX_PROGRAM_NV );

}

/*
GL_DisableSpecularShaderGF3() ??
Same as GL_DisableDiffuseShaderGF3()
*/
void GL_EnableAttentShaderGF3(vec3_t lightOrig)
{
    float invrad = 1 / currentshadowlight->radius;
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(0.5,0.5,0.5);
    glScalef(0.5,0.5,0.5);
    glScalef(invrad, invrad, invrad);
    glTranslatef(-lightOrig[0], -lightOrig[1], -lightOrig[2]);

    glDisable(GL_TEXTURE_2D);
    glEnable(GL_TEXTURE_3D);
    glBindTexture(GL_TEXTURE_3D, atten3d_texture_object);
}

void GL_DisableAttentShaderGF3(void)
{
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_TEXTURE_3D);
    glEnable(GL_TEXTURE_2D);
}

void R_DrawWorldGF3Diffuse(lightcmd_t *lightCmds)
{
    int         command, num, i;
    int         lightPos = 0;
    msurface_t  *surf;
    float		*v;
    texture_t	*t;//XYZ

    //support flickering lights
    while (1)
    {
        command = lightCmds[lightPos++].asInt;

        if (command == 0) break; //end of list

        surf = lightCmds[lightPos++].asVoid;

        if (surf->visframe != r_framecount)
        {
            lightPos+=(4+surf->polys->numverts*(2+3));
            continue;
        }
        num = surf->polys->numverts;

        lightPos += 4;//skip color

        //XYZ
        t = R_TextureAnimation (surf->texinfo->texture);

        GL_SelectTexture(GL_TEXTURE0_ARB);
        GL_Bind(t->gl_texturenum+1);
        GL_SelectTexture(GL_TEXTURE2_ARB);
        GL_Bind(t->gl_texturenum);

        glBegin(command);

        for (i=0, v = (float *)(&globalVertexTable[surf->polys->firstvertex]); i<num; i++, v+= VERTEXSIZE)
        {
            //skip attent texture coord.
            lightPos += 2;
            qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]);
            qglMultiTexCoord3fvARB(GL_TEXTURE1_ARB, &lightCmds[lightPos].asFloat);
            lightPos += 3;
            glVertex3fv(&v[0]);
        }
        glEnd();
    }
    GL_SelectTexture(GL_TEXTURE0_ARB);
}

void R_DrawWorldGF3Specular(lightcmd_t *lightCmds)
{
    int         command, num, i;
    int         lightPos = 0;
    vec3_t      tsH,H;
    float       *lightP;
    msurface_t  *surf;
    float		*v;
    vec3_t      lightDir;
    texture_t	*t;//XYZ

    //support flickering lights
    while (1)
    {
        command = lightCmds[lightPos++].asInt;

        if (command == 0) break; //end of list

        surf = lightCmds[lightPos++].asVoid;

        if (surf->visframe != r_framecount)
        {
            lightPos+=(4+surf->polys->numverts*(2+3));
            continue;
        }
        num = surf->polys->numverts;

        lightPos += 4;//skip color

        t = R_TextureAnimation (surf->texinfo->texture);

        GL_SelectTexture(GL_TEXTURE0_ARB);
        GL_Bind(t->gl_texturenum+1);
        GL_SelectTexture(GL_TEXTURE2_ARB);
        GL_Bind(t->gl_texturenum);

        glBegin(command);

        for (i=0, v = (float *)(&globalVertexTable[surf->polys->firstvertex]); i<num; i++, v+= VERTEXSIZE)
        {
            lightPos += 2;//skip texcoords
            lightP = &lightCmds[lightPos].asFloat;
            VectorCopy(lightP, lightDir);
            Normalize(lightDir);
            lightPos += 3;

            //calculate local H vector and put it into tangent space
            //r_origin = camera position
            VectorSubtract(r_refdef.vieworg, v, H);
            Normalize(H);

            //put H in tangent space firste since lightDir (precalc) is already in tang space
            if (surf->flags & SURF_PLANEBACK)
            {
                tsH[2] = -DotProduct(H, surf->plane->normal);
            }
            else
            {
                tsH[2] = DotProduct(H, surf->plane->normal);
            }
            tsH[1] = -DotProduct(H, surf->texinfo->vecs[1]);
            tsH[0] = DotProduct(H, surf->texinfo->vecs[0]);

            VectorAdd(lightDir, tsH, tsH);

            qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]);
            qglMultiTexCoord3fvARB(GL_TEXTURE1_ARB, &tsH[0]);
            glVertex3fv(&v[0]);
        }
        glEnd();
    }
    GL_SelectTexture(GL_TEXTURE0_ARB);
}

void R_DrawBrushGF3Diffuse(entity_t *e)
{
    model_t	            *model = e->model;
    msurface_t          *surf;
    glpoly_t	        *poly;
    int		            i, j, count;
    brushlightinstant_t *ins = (brushlightinstant_t *) e->brushlightinstant;
    float	            *v;
    texture_t           *t; //XYZ

    count = 0;

    for (i=0, surf = &model->surfaces[model->firstmodelsurface]; i<model->nummodelsurfaces; i++, surf++)
    {
        if (!ins->polygonVis[i]) continue;

        poly = surf->polys;

        //XYZ
        t = R_TextureAnimation (surf->texinfo->texture);

        GL_SelectTexture(GL_TEXTURE0_ARB);
        GL_Bind(t->gl_texturenum+1);
        GL_SelectTexture(GL_TEXTURE2_ARB);
        GL_Bind(t->gl_texturenum);

        glBegin(GL_TRIANGLE_FAN);

        for (j=0, v = (float *)(&globalVertexTable[poly->firstvertex]) ; j<poly->numverts ; j++, v+= VERTEXSIZE)
        {
            qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]);
            qglMultiTexCoord3fvARB(GL_TEXTURE1_ARB, &ins->tslights[count+j][0]);
            glVertex3fv(v);
        }
        glEnd();

        count += surf->numedges;
    }
}

void R_DrawBrushGF3Specular(entity_t *e)
{
    model_t	            *model = e->model;
    msurface_t          *surf;
    glpoly_t	        *poly;
    int		            i, j, count;
    brushlightinstant_t *ins = (brushlightinstant_t *) e->brushlightinstant;
    float	            *v;
    texture_t	        *t;//XYZ

    count = 0;

    for (i=0, surf = &model->surfaces[model->firstmodelsurface]; i<model->nummodelsurfaces; i++, surf++)
    {
        if (!ins->polygonVis[i]) continue;

        poly = surf->polys;

        t = R_TextureAnimation (surf->texinfo->texture);

        GL_SelectTexture(GL_TEXTURE0_ARB);
        GL_Bind(t->gl_texturenum+1);
        GL_SelectTexture(GL_TEXTURE2_ARB);
        GL_Bind(t->gl_texturenum);

        glBegin(GL_TRIANGLE_FAN);

        for (j=0, v = (float *)(&globalVertexTable[poly->firstvertex]) ; j<poly->numverts ; j++, v+= VERTEXSIZE)
        {
            qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]);
            qglMultiTexCoord3fvARB(GL_TEXTURE1_ARB, &ins->tshalfangles[count+j][0]);
            glVertex3fv(v);
        }
        glEnd();

        count += surf->numedges;
    }
}

void R_DrawAliasFrameGF3Diffuse (aliashdr_t *paliashdr, aliasframeinstant_t *instant)
{
    fstvert_t	        *texcoords;
    int                 anim;
    int			        *indecies;
    aliaslightinstant_t *linstant = (aliaslightinstant_t *) instant->lightinstant;

    texcoords = (fstvert_t *)((byte *)paliashdr + paliashdr->texcoords);

    //bind normal map
    anim = (int)(cl.time*10) & 3;

    GL_SelectTexture(GL_TEXTURE0_ARB);
    GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]+1);
    GL_SelectTexture(GL_TEXTURE2_ARB);
    GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]);

    indecies = (int *)((byte *)paliashdr + paliashdr->indecies);

    glVertexPointer(3, GL_FLOAT, 0, instant->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE1_ARB);
    glTexCoordPointer(3, GL_FLOAT, 0, linstant->tslights);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glDrawElements(GL_TRIANGLES, linstant->numtris * 3, GL_UNSIGNED_INT, &linstant->indecies[0]);

    if (sh_noshadowpopping.value)
    {
        glStencilFunc(GL_LEQUAL, 1, 0xffffffff);
        glDrawElements(GL_TRIANGLES, (paliashdr->numtris*3) - (linstant->numtris*3), GL_UNSIGNED_INT, &linstant->indecies[linstant->numtris*3]);
        glStencilFunc(GL_EQUAL, 0, 0xffffffff);
    }
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    GL_SelectTexture(GL_TEXTURE0_ARB);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void R_DrawAliasFrameGF3Specular (aliashdr_t *paliashdr, aliasframeinstant_t *instant)
{
    fstvert_t	        *texcoords;
    int                 anim;
    int                 *indecies;
    aliaslightinstant_t *linstant = (aliaslightinstant_t *) instant->lightinstant;

    texcoords = (fstvert_t *)((byte *)paliashdr + paliashdr->texcoords);

    //bind normal map
    anim = (int)(cl.time*10) & 3;

    GL_SelectTexture(GL_TEXTURE0_ARB);
    GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]+1);
    GL_SelectTexture(GL_TEXTURE2_ARB);
    GL_Bind(paliashdr->gl_texturenum[currententity->skinnum][anim]);

    indecies = (int *)((byte *)paliashdr + paliashdr->indecies);

    glVertexPointer(3, GL_FLOAT, 0, instant->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE1_ARB);
    glTexCoordPointer(3, GL_FLOAT, 0, linstant->tshalfangles);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    //to to correct self shadowing on alias models send the light vectors an extra time...
    qglClientActiveTextureARB(GL_TEXTURE2_ARB);
    glTexCoordPointer(3, GL_FLOAT, 0, linstant->tslights);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glDrawElements(GL_TRIANGLES, linstant->numtris*3, GL_UNSIGNED_INT, &linstant->indecies[0]);

    if (sh_noshadowpopping.value)
    {
        glStencilFunc(GL_LEQUAL, 1, 0xffffffff);
        glDrawElements(GL_TRIANGLES, (paliashdr->numtris*3) - (linstant->numtris*3), GL_UNSIGNED_INT, &linstant->indecies[linstant->numtris*3]);
        glStencilFunc(GL_EQUAL, 0, 0xffffffff);
    }
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    qglClientActiveTextureARB(GL_TEXTURE1_ARB);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    GL_SelectTexture(GL_TEXTURE0_ARB);
    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void R_DrawWorldBumpedGF3(void)
{
    if (!currentshadowlight->visible) return;

    glDepthMask (GL_FALSE);
    glShadeModel (GL_SMOOTH);

    if (currentshadowlight->filtercube)
    {
        //draw attent into dest alpha
        GL_DrawAlpha();
        GL_EnableAttentShaderGF3(currentshadowlight->origin);
        R_DrawWorldWV(currentshadowlight->lightCmds, false);
        GL_DisableAttentShaderGF3();
        GL_ModulateAlphaDrawColor();
    }
    else
    {
        GL_AddColor();
    }
    glColor3fv(&currentshadowlight->color[0]);

    GL_EnableSpecularShaderGF3(true,currentshadowlight->origin,false);
    R_DrawWorldGF3Specular(currentshadowlight->lightCmds);
    GL_DisableDiffuseShaderGF3();

    GL_EnableDiffuseShaderGF3(true,currentshadowlight->origin);
    R_DrawWorldGF3Diffuse(currentshadowlight->lightCmds);
    GL_DisableDiffuseShaderGF3();

    glColor3f (1,1,1);
    glDisable (GL_BLEND);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glDepthMask (GL_TRUE);
}

void R_DrawBrushBumpedGF3(entity_t *e)
{
    if (currentshadowlight->filtercube)
    {
        //draw attent into dest alpha
        GL_DrawAlpha();
        GL_EnableAttentShaderGF3(((brushlightinstant_t *)e->brushlightinstant)->lightpos);
        R_DrawBrushWV(e, false);
        GL_DisableAttentShaderGF3();
        GL_ModulateAlphaDrawColor();
    }
    else
    {
        GL_AddColor();
    }
    glColor3fv(&currentshadowlight->color[0]);

    GL_EnableSpecularShaderGF3(false,((brushlightinstant_t *)e->brushlightinstant)->lightpos,false);
    R_DrawBrushGF3Specular(e);
    GL_DisableDiffuseShaderGF3();

    GL_EnableDiffuseShaderGF3(false,((brushlightinstant_t *)e->brushlightinstant)->lightpos);
    R_DrawBrushGF3Diffuse(e);
    GL_DisableDiffuseShaderGF3();
}

void R_DrawAliasBumpedGF3(aliashdr_t *paliashdr, aliasframeinstant_t *instant)
{
    if (currentshadowlight->filtercube)
    {
        //draw attent into dest alpha
        GL_DrawAlpha();
        GL_EnableAttentShaderGF3(instant->lightinstant->lightpos);
        R_DrawAliasFrameWV(paliashdr,instant, false);
        GL_DisableAttentShaderGF3();
        GL_ModulateAlphaDrawColor();
    }
    else
    {
        GL_AddColor();
    }
    glColor3fv(&currentshadowlight->color[0]);

    GL_EnableSpecularShaderGF3(false,instant->lightinstant->lightpos,true);
    R_DrawAliasFrameGF3Specular(paliashdr,instant);
    GL_DisableDiffuseShaderGF3();

    GL_EnableDiffuseShaderGF3(false,instant->lightinstant->lightpos);
    R_DrawAliasFrameGF3Diffuse(paliashdr,instant);
    GL_DisableDiffuseShaderGF3();
}

/*
	Vertex programs
*/


/*
  Thisone does not do anything too usefull, it just copyies some things around
  instead of sending the save coordinates for unit0/2 we send them once and copy
  them here, this saves some bandwith and is slightly faster.
*/
char vpDiffuseGF3 [] =
    "!!VP1.1 # Diffuse bumpmapping vetex program.\n"
    "OPTION NV_position_invariant;"
    // Generates a necessary input for the diffuse bumpmapping registers
    //
    // c[0]...c[3]      contains the modelview projection composite matrix
    // c[4]...c[7]      contains the texture matrix of unit 3
    // v[OPOS]          contains the per-vertex position
    // v[TEX1]          contains the per-vertex tangent space light vector
    // v[TEX0]          contains the per-vertex texture coordinate 0

    // o[HPOS]          output register for homogeneous position
    // o[TEX0]          output register for texture coordinate 0
    // o[TEX1]          output register for texture coordinate 1
    // o[TEX2]          output register for texture coordinate 2
    // o[TEX3]          output register for texture coordinate 3

    // Transform vertex to view-space

    // Transform vertex by texture matrix and copy to output
    "DP4   o[TEX3].x, v[OPOS], c[4];"
    "DP4   o[TEX3].y, v[OPOS], c[5];"
    "DP4   o[TEX3].z, v[OPOS], c[6];"
    "DP4   o[TEX3].w, v[OPOS], c[7];"

    //copy tex coords of unit 0 to unit 2
    "MOV   o[TEX0], v[TEX0];"
    "MOV   o[TEX2], v[TEX0];"

    "MOV   o[TEX1], v[TEX1];"

    "MOV   o[COL0], v[COL0];"

    "END";

/*
	Only used for specular on alias models when noshadowpopping is enabled...
*/
char vpSpecularAliasGF3 [] =
    "!!VP1.1 # Diffuse bumpmapping vetex program.\n"
    "OPTION NV_position_invariant;"
    // Generates a necessary input for the diffuse bumpmapping registers
    //
    // c[0]...c[3]      contains the modelview projection composite matrix
    // c[4]...c[7]      contains the texture matrix of unit 3
    // v[OPOS]          contains the per-vertex position
    // v[TEX1]          contains the per-vertex tangent space light vector
    // v[TEX0]          contains the per-vertex texture coordinate 0
    // v[TEX2]		  contains the per-vertex light vector

    // o[HPOS]          output register for homogeneous position
    // o[TEX0]          output register for texture coordinate 0
    // o[TEX1]          output register for texture coordinate 1
    // o[TEX2]          output register for texture coordinate 2
    // o[TEX3]          output register for texture coordinate 3

    // Transform vertex to view-space

    // Transform vertex by texture matrix and copy to output
    "DP4   o[TEX3].x, v[OPOS], c[4];"
    "DP4   o[TEX3].y, v[OPOS], c[5];"
    "DP4   o[TEX3].z, v[OPOS], c[6];"
    "DP4   o[TEX3].w, v[OPOS], c[7];"


    "MOV   o[TEX0], v[TEX0];"

    //range compress into secondary color
    "MAD   o[COL1], v[TEX2], c[20], c[20];"

    //copy tex coords of unit 0 to unit 2
    "MOV   o[TEX2], v[TEX0];"

    "MOV   o[TEX1], v[TEX1];"

    "MOV   o[COL0], v[COL0];"

    "END";

void R_LoadVertexProgram(void)
{
    GLint errPos, errCode;
    const GLubyte *errString;

    if ( gl_cardtype != GEFORCE3 ) return;

    // Create the vertex program.
    qglGenProgramsNV( 1, &diffuse_program_object);

    qglLoadProgramNV( GL_VERTEX_PROGRAM_NV, diffuse_program_object, strlen(vpDiffuseGF3), (const GLubyte *) vpDiffuseGF3);

    qglGenProgramsNV( 1, &specularalias_program_object);

    qglLoadProgramNV( GL_VERTEX_PROGRAM_NV, specularalias_program_object, strlen(vpSpecularAliasGF3), (const GLubyte *) vpSpecularAliasGF3);

    if ( (errCode = glGetError()) != GL_NO_ERROR )
    {
        errString = gluErrorString( errCode );
        glGetIntegerv( GL_PROGRAM_ERROR_POSITION_NV, &errPos);
        Con_Printf("LoadVertexProgram: %s\n", errString);
        Con_Printf("error is located at line: %d\n", errPos);
        exit( -1 );
    }
    else
    {
        Con_Printf("VertexProgram loaded\n");
    }


    // Track the concatenation of the modelview and projection matrix in registers 0-3.
    qglTrackMatrixNV( GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW_PROJECTION_NV, GL_IDENTITY_NV );

    // Track the texture unit 3 maxtix in registers 4-7
    qglTrackMatrixNV( GL_VERTEX_PROGRAM_NV, 4, GL_TEXTURE3_ARB, GL_IDENTITY_NV );

    //store 0.5 0.5 0.5 0.5 in register 8
    qglProgramParameter4fNV( GL_VERTEX_PROGRAM_NV, 20, 0.5, 0.5, 0.5, 0.5);
}
